package genedit.controller.interfaceActions;

import genedit.gui.GMainFrame;
import genedit.model.Workspace;
import genedit.model.graph.BreakPoint;
import genedit.model.graph.GraphElement;
import genedit.model.graph.Link;
import genedit.view.graph.GraphDocumentView;
import genedit.view.graph.GraphWorkArea;

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.geom.Point2D;
import java.util.Iterator;

import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.KeyStroke;

/**
 * Akcija koja zumira radnu površinu tako da obuhvati sve selektovane elemente,
 * odnosno sve elemente ukoliko nema selektovanih, vodeći računa na dozvoljene
 * granice zumiranja.
 * 
 * @author Nikola
 * @version 1.1 18/12/13
 */
public class GZoomBestFit extends AbstractAction {

	private static final long serialVersionUID = 1L;

	/**
	 * Singleton instanca akcije.
	 */
	private static GZoomBestFit instance = null;

	public GZoomBestFit() {
		putValue(NAME, "Zoom Best Fit");
		putValue(SMALL_ICON, new ImageIcon("resources/icons/zoom-fit-best.png"));
		putValue(MNEMONIC_KEY, KeyEvent.VK_PLUS);
		putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, ActionEvent.CTRL_MASK));
		putValue(SHORT_DESCRIPTION, "Zoom Best Fit");
	}

	public static AbstractAction getInstance() {
		if (instance == null)
			instance = new GZoomBestFit();
		return instance;
	}

	/**
	 * Akcija izračunava minimalne i maksimalne X i Y koordinate selektovanih,
	 * odnosno svih elemenata. Zatim ih poredi sa trenutnom veličinom radne
	 * površine i sračunava faktor zumiranja. Pored transforamcije skaliranja
	 * primenjuje se i translacija, jer je potrebno radnu površinu i
	 * pozicionirati na lokaciju elemenata.
	 */
	@Override
	public void actionPerformed(ActionEvent e) {
		GraphDocumentView document = Workspace.getInstance().getActiveDocument();
		if (document == null || document.getElementPainters().size() == 0)
			return;

		Dimension windowSize = GMainFrame.getInstance().getGraphDocumentViewDimension(
				document.getModel());

		Iterator<GraphElement> it = null;

		if (document.getModel().getSelectedElements().isEmpty())
			it = document.getModel().getIteratorElements();
		else
			it = document.getModel().getIteratorSelectedElements();

		double minX = Double.MAX_VALUE, maxX = Double.MIN_VALUE;
		double minY = Double.MAX_VALUE, maxY = Double.MIN_VALUE;

		while (it.hasNext()) {
			GraphElement currentElement = it.next();
			if (!(currentElement instanceof Link)) {
				Point2D positionOfElement = currentElement.getPosition();
				Dimension sizeOfElement = currentElement.getSize();

				minX = Math.min(minX, positionOfElement.getX());
				maxX = Math.max(maxX, positionOfElement.getX() + sizeOfElement.getWidth());
				minY = Math.min(minY, positionOfElement.getY());
				maxY = Math.max(maxY, positionOfElement.getY() + sizeOfElement.getHeight());
			} else {
				Link linkElement = (Link) currentElement;
				Iterator<BreakPoint> itPoints = linkElement.getIteratorBreakPoints();
				while (itPoints.hasNext()) {
					BreakPoint bp = itPoints.next();
					minX = Math.min(minX, bp.getPosition().getX() - 30);
					maxX = Math.max(maxX, bp.getPosition().getX() - 15);
					minY = Math.min(minY, bp.getPosition().getY() - 30);
					maxY = Math.max(maxY, bp.getPosition().getY() - 15);
				}
			}
		}

		double neededWidth = maxX - minX + 30;
		double neededHeight = maxY - minY + 30;

		double scaleFactor1 = windowSize.getHeight() / neededHeight;
		double scaleFactor2 = windowSize.getWidth() / neededWidth;
		
		double scaleFactor = Math.min(scaleFactor1,scaleFactor2);


		GraphWorkArea workArea = ((GraphWorkArea) document.getWorkArea());
		workArea.setScaling(scaleFactor * 0.95);
		workArea.setTranslateX(-minX);
		workArea.setTranslateY(-minY);
		workArea.setupTransformation();
		workArea.repaint();
	}
}
